package com.ideaaedi.component.decompile;

import com.google.common.collect.Lists;
import com.ideaaedi.commonds.clazz.ClassNameGrabber;
import com.ideaaedi.component.decompile.cfr.CfrDecompiler;
import com.ideaaedi.component.decompile.fernflower.FfDecompiler;
import org.apache.commons.lang3.tuple.Triple;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 反编译器
 *
 * @author JustryDeng
 * @since 2021/9/23 22:59:27
 */
public interface Decompiler {
    
    /**
     * 获取默认的反编译器<br><br>
     * 目前可用的反编译器有：
     * <ul>
     *     <li>{@link FfDecompiler}：基于fernflower的反编译器</li>
     *     <li>{@link CfrDecompiler}：基于cfr的反编译器</li>
     * </ul>
     *
     * @return  默认的反编译器
     */
    static Decompiler defaultDecompiler() {
        return new FfDecompiler();
    }
    
    /**
     * CFR反编译器<br>
     *
     * @return  CFR反编译器
     */
    static Decompiler cfrDecompiler() {
        return new CfrDecompiler();
    }
    
    /**
     * @see Decompiler#decompile(Collection, String)
     */
    default String decompile(byte[] classByte) {
        return decompile(classByte, null);
    }

    /**
     * @see Decompiler#decompile(Collection, String)
     */
    default String decompile(byte[] classByte, String methodName){
        Objects.requireNonNull(classByte, "classByte cannot be null.");
        return decompile(Lists.newArrayList(classByte), methodName).values().iterator().next();
    }
    
    /**
     * @see Decompiler#decompile(Collection, String)
     */
    default Set<String> decompile(Collection<byte[]> classByteCollection){
        return new HashSet<>(decompile(classByteCollection, null).values());
    }
    
    /**
     * 反编译获取源码并解析全类名<br>
     * @see Decompiler#decompile(Collection, String)
     *
     * @param classByteCollection
     *            待反编译的class
     * @return <ul>
     *     <li>左 - 全类名<br>形如：com.szlzcl.jdk8feature.OptionalTests.MyConsumer</li>
     *     <li>中 - 源码的classFilePath<br>形如：com/szlzcl/jdk8feature/OptionalTests$MyConsumer.class</li>
     *     <li>右 - 源码<br>注意：反编译失败或找不存要反编译的目标方法时，值为空白字符串</li>
     * </ul>
     */
    default List<Triple<String, String, String>> decompileAsTriple(Collection<byte[]> classByteCollection){
        Map<String, String> map = decompile(classByteCollection, null);
        List<Triple<String, String, String>> list = new ArrayList<>(map.size());
        String classFilePath;
        String sourceCode;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            classFilePath = entry.getKey();
            sourceCode = entry.getValue();
            list.add(
                    Triple.of(ClassNameGrabber.obtainFromSourceCode(sourceCode), classFilePath, sourceCode)
            );
        }
        return list;
    }
    
    /**
     * 反编译
     *
     * @param classByteCollection
     *            待反编译的字节码集合
     * @param methodName
     *            只反编译指定的方法<br>
     *            <ul>
     *                <li>{@link FfDecompiler}: 不支持此参数，当时用FfDecompiler时，直接全部反编译整个类</li>
     *                <li>{@link CfrDecompiler}: 支持此参数</li>
     *            </ul>
     * @return  反编译出来的<b>源码的classFilePath</b>以及<b>源码</b>.
     *          注意：<ul>
     *                  <li>key - 源码对应class的classFilePath, 形如com/szlzcl/jdk8feature/OptionalTests$MyConsumer.class</li>
     *                  <li>value - 源码</li>
     *               </ul>
     */
    LinkedHashMap<String, String> decompile(Collection<byte[]> classByteCollection, String methodName);
}
